using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Events;
using Volo.Abp;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.AspNetCore.Serilog;
using Volo.Abp.AspNetCore.Tracing;
using Volo.Abp.Autofac;
using Volo.Abp.Caching;
using Volo.Abp.Caching.StackExchangeRedis;
using Volo.Abp.DataProtection;
using Volo.Abp.EventBus.RabbitMq;
using Volo.Abp.Modularity;
using Volo.Abp.Swashbuckle;
using Volo.Abp.Tracing;
using Volo.Abp.VirtualFileSystem;
using Matrix.ServiceDiscovery;
using Microsoft.Extensions.Configuration;
using Microsoft.OpenApi.Models;
using Volo.Abp.AspNetCore.Authentication.JwtBearer;
using Volo.Abp.BackgroundWorkers;
using Volo.Abp.BackgroundJobs.RabbitMQ;
using Volo.Abp.DistributedLocking;
using Volo.Abp.DistributedLocking.Redis;
using Volo.Abp.OpenTelemetry;
using Volo.Abp.Threading;

namespace MatrixServiceDiscovery.HttpApi.Host
{
    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpCachingStackExchangeRedisModule),
        typeof(AbpAspNetCoreMvcModule),
        typeof(AbpAspNetCoreAuthenticationJwtBearerModule),
        typeof(AbpAspNetCoreSerilogModule),
        typeof(AbpSwashbuckleModule),
        typeof(AbpEventBusRabbitMqModule),
        typeof(AbpBackgroundJobsRabbitMqModule),
        typeof(AbpDistributedLockingRedisModule),
        typeof(AbpOpenTelemetryModule),
        typeof(AbpAspNetCoreTracingModule),
        typeof(AbpDataProtectionModule),
        typeof(MatrixServiceDiscoveryHttpApiModule)
    )]
    public class MatrixServiceDiscoveryHttpApiHostModule : AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();

            PreConfigure<AbpAspNetCoreMvcOptions>(options =>
            {
                options.ConventionalControllers.Create(typeof(MatrixServiceDiscoveryHttpApiHostModule).Assembly);
            });

            PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
            {
                options.AddAssemblyResource(
                    typeof(MatrixServiceDiscoveryHttpApiHostModule).Assembly,
                    MatrixServiceDiscoveryResourceConsts.LocalizationResourceName
                );
            });

            PreConfigure<OpenApiInfo>(options =>
            {
                options.Title = "Matrix Service Discovery API";
                options.Version = "v1.0";
                options.Description = "Matrix Framework 服务发现模块 HTTP API";
                options.Contact = new OpenApiContact
                {
                    Name = "Matrix Framework Team",
                    Email = "team@matrix-framework.com",
                    Url = "https://matrix-framework.com"
                };
                options.License = new OpenApiLicense
                {
                    Name = "MIT",
                    Url = "https://opensource.org/licenses/MIT"
                };
            });
        }

        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var hostingEnvironment = context.Services.GetHostingEnvironment();
            var configuration = context.Services.GetConfiguration();

            ConfigureCache(context);
            ConfigureCors(context, configuration);
            ConfigureDataProtection(context, configuration);
            ConfigureDistributedLocking(context, configuration);
            ConfigureEventBus(context, configuration);
            ConfigureOpenTelemetry(context, configuration);
            ConfigureHealthChecks(context, configuration);
            ConfigureSwagger(context, configuration);
            ConfigureBackgroundJobs(context, configuration);
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();
            var env = context.GetEnvironment();

            // 基础中间件
            app.UseCorrelationId();
            app.UseStaticFiles();
            app.UseRouting();
            app.UseCors();
            app.UseAuthentication();
            app.UseAuthorization();

            // API文档中间件
            if (env.IsDevelopment())
            {
                app.UseSwagger();
                app.UseSwaggerUI(options =>
                {
                    options.SwaggerEndpoint("/swagger/v1/swagger.json", "Matrix Service Discovery API");
                    options.RoutePrefix = "swagger";
                    options.DocumentTitle = "Matrix Service Discovery API Documentation";
                });
            }

            // 健康检查中间件
            app.UseHealthChecks("/health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
            {
                ResponseWriter = WriteHealthCheckResponse
            });

            app.UseHealthChecks("/health/ready", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
            {
                Predicate = check => check.Tags.Contains("ready"),
                ResponseWriter = WriteHealthCheckResponse
            });

            app.UseHealthChecks("/health/live", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
            {
                Predicate = check => check.Tags.Contains("live"),
                ResponseWriter = WriteHealthCheckResponse
            });

            // ABP中间件
            app.UseAbpSerilogEnrichers();
            app.UseConfiguredEndpoints();

            // 后台任务
            context.ServiceProvider
                .GetRequiredService<IBackgroundWorkerManager>()
                .StartAsync();
        }

        private void ConfigureCache(ServiceConfigurationContext context)
        {
            Configure<AbpDistributedCacheOptions>(options =>
            {
                options.KeyPrefix = "Matrix:ServiceDiscovery";
            });

            var configuration = context.Services.GetConfiguration();
            var redisConnectionString = configuration.GetConnectionString("Redis") ??
                configuration["Redis:Configuration"];

            if (!string.IsNullOrWhiteSpace(redisConnectionString))
            {
                context.Services.AddStackExchangeRedisCache(options =>
                {
                    options.Configuration = redisConnectionString;
                });
            }
        }

        private void ConfigureCors(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddCors(options =>
            {
                options.AddDefaultPolicy(builder =>
                {
                    builder
                        .WithOrigins(
                            configuration["App:CorsOrigins"]?
                                .Split(",", StringSplitOptions.RemoveEmptyEntries)
                                .Select(o => o.RemovePostFix("/"))
                                .ToArray() ??
                            new[] { "https://*.matrix-framework.com", "http://localhost:4200" })
                        .SetIsOriginAllowedToAllowWildcardSubdomains()
                        .WithAbpExposedHeaders()
                        .SetIsOriginAllowedToAllowWildcardSubdomains()
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials();
                });
            });
        }

        private void ConfigureDataProtection(ServiceConfigurationContext context, IConfiguration configuration)
        {
            var dataProtectionBuilder = context.Services.AddDataProtection().SetApplicationName("MatrixServiceDiscovery");

            var redisConnectionString = configuration.GetConnectionString("Redis");
            if (!string.IsNullOrWhiteSpace(redisConnectionString))
            {
                var redis = StackExchange.Redis.ConnectionMultiplexer.Connect(redisConnectionString);
                dataProtectionBuilder.PersistKeysToStackExchangeRedis(redis, "MatrixServiceDiscovery:DataProtection:Keys");
            }
        }

        private void ConfigureDistributedLocking(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddSingleton<AbpDistributedLockOptions>(sp =>
            {
                var options = new AbpDistributedLockOptions();
                configuration.GetSection("DistributedLock").Bind(options);
                return options;
            });
        }

        private void ConfigureEventBus(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpRabbitMqEventBus(options =>
            {
                options.ConnectionFactoryName = "Default";
                options.ExchangeName = "MatrixServiceDiscovery";
                options.ClientId = configuration["RabbitMQ:ClientId"] ?? "MatrixServiceDiscovery";
            });
        }

        private void ConfigureOpenTelemetry(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpOpenTelemetry(options =>
            {
                options.Configure(resourceBuilder =>
                {
                    resourceBuilder
                        .AddService("MatrixServiceDiscovery")
                        .AddAttributes(new Dictionary<string, object>
                        {
                            ["service.name"] = "MatrixServiceDiscovery",
                            ["service.namespace"] = "Matrix",
                            ["service.instance.id"] = Environment.MachineName,
                            ["service.version"] = "1.0.0"
                        });
                });

                if (configuration.GetValue<bool>("OpenTelemetry:EnableConsoleExporter"))
                {
                    options.AddConsoleExporter();
                }

                var otlpEndpoint = configuration["OpenTelemetry:OtlpEndpoint"];
                if (!string.IsNullOrWhiteSpace(otlpEndpoint))
                {
                    options.AddOtlpExporter(otlpOptions =>
                    {
                        otlpOptions.Endpoint = new Uri(otlpEndpoint);
                    });
                }
            });
        }

        private void ConfigureHealthChecks(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddHealthChecks()
                .AddCheck<MatrixServiceDiscoveryHealthCheck>("matrix-service-discovery", tags: new[] { "ready", "live" });

            // 数据库健康检查
            var connectionString = configuration.GetConnectionString("Default");
            if (!string.IsNullOrWhiteSpace(connectionString))
            {
                context.Services.AddHealthChecks()
                    .AddNpgSql(connectionString, name: "postgresql", tags: new[] { "ready" });
            }

            // Redis健康检查
            var redisConnectionString = configuration.GetConnectionString("Redis") ?? configuration["Redis:Configuration"];
            if (!string.IsNullOrWhiteSpace(redisConnectionString))
            {
                context.Services.AddHealthChecks()
                    .AddRedis(redisConnectionString, name: "redis", tags: new[] { "ready" });
            }

            // RabbitMQ健康检查
            var rabbitMqConnectionString = configuration.GetConnectionString("RabbitMQ");
            if (!string.IsNullOrWhiteSpace(rabbitMqConnectionString))
            {
                context.Services.AddHealthChecks()
                    .AddRabbitMQ(rabbitMqConnectionString, name: "rabbitmq", tags: new[] { "ready" });
            }
        }

        private void ConfigureSwagger(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpSwaggerGenWithOAuth(
                "MatrixServiceDiscovery",
                options =>
                {
                    options.SwaggerDoc("v1", new OpenApiInfo
                    {
                        Title = "Matrix Service Discovery API",
                        Version = "v1.0",
                        Description = "Matrix Framework 服务发现模块 HTTP API 文档"
                    });

                    options.DocInclusionPredicate((docName, description) => true);
                    options.CustomSchemaIds(type => type.FullName);
                },
                tokenUrl: configuration["AuthServer:TokenEndpoint"] ?? "/connect/token"
            );
        }

        private void ConfigureBackgroundJobs(ServiceConfigurationContext context, IConfiguration configuration)
        {
            context.Services.AddAbpBackgroundJob<RabbitMQBackgroundJobOptions>(options =>
            {
                options.ConnectionFactoryName = "Default";
                options.DefaultQueue = "MatrixServiceDiscovery";
            });
        }

        private static Task WriteHealthCheckResponse(HttpContext httpContext, Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthReport result)
        {
            httpContext.Response.ContentType = "application/json";

            var response = new
            {
                status = result.Status.ToString(),
                results = result.Entries.Select(x => new { key = x.Key, status = x.Value.Status.ToString() }),
                timestamp = DateTime.UtcNow,
                duration = result.TotalDuration.TotalMilliseconds
            };

            return httpContext.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(response));
        }
    }
}